﻿// -----------------------------------------------------------------------
// <copyright file="FreeFrame.cs" company="">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------

namespace ManagedFreeFrame
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.Globalization;
    using System.IO;
    using System.Runtime.InteropServices;

    using ManagedFreeFrame.Enum;
    using ManagedFreeFrame.Struct;

    // ReSharper disable MemberCanBeInternal
    /// <summary>
    /// FreeFrame Plugins export a single function: plugMain. 
    /// This is passed 3 values: a 32-bit function code, a 32-bit input value and a 32-bit instance identifier. 
    /// It returns a 32-bit output value. 
    /// The input values and output values have different types according to the function code. 
    /// This may be implemented differently on different platforms in different languages, but the format of the values must be consistent. 
    /// 
    /// PlugMain(unsigned integer functionCode, unsigned integer inputValue, unsigned integer instanceID) 
    /// Parameters:
    /// - functionCode: Tells the plugin which function is being called (see function table) 
    /// - inputValue: 32-bit value or 32-bit pointer to some structure, depending on function code.  
    /// - instanceID: 32-bit instance identifier. Only used for instance specific functions (see function table) 
    /// 
    /// The following types are used:
    /// Type Range of type Notes 
    /// - 32-bit unsigned integer 0 to 4294967295 - 
    /// - 32-bit pointer 0 to 4294967295 needs to be a valid memory location 
    /// - 32-bit IEEE float -lots to +lots 1 sign bit, 8 bit exponent, 24 bit mantissa 
    /// </summary>
    public sealed class FreeFrame
    {
        #region declare
        /// <summary>
        ///   a .net function that resembles the the "plugMain"-function in the loaded library
        /// </summary>
        private readonly PlugMainInvoker _plugMainInvoker;

        private readonly PlugMainInvoker2 _plugMainInvoker2;

        /// <summary>
        ///   a '.net'->'loaded-plugin' function , in the shape of the "plugMain"
        /// </summary>
        /// <param name = "functionCode">Tells the plugin which function is being called (see function table in pdf).</param>
        /// <param name = "inputValue">32-bit value or 32-bit pointer to some structure, depending on function code.</param>
        /// <param name = "instanceId">32-bit instance identifier. Only used for instance specific functions (see function table in pdf).</param>
        /// <returns>Depends on function code. See function table in pdf for details.</returns>
        /// <remarks>
        ///   This is the one and only function exposed by the library containing the actual plugin.
        /// </remarks>
        private delegate IntPtr PlugMainInvoker(UInt32 functionCode, UInt32 inputValue, UInt32 instanceId);

        private delegate IntPtr PlugMainInvoker2(UInt32 functionCode, IntPtr inputValue, UInt32 instanceId);
        #endregion

        #region Initialze
// ReSharper disable MemberCanBeInternal
        public FreeFrame(FileInfo file)
// ReSharper restore MemberCanBeInternal
        {
            this.Parameters = new List<Parameter>();

            // remember the libary
            if (file.Extension.Equals(".dll") == false)
            {
                throw new ArgumentException("Given file is not a dll", "file");
            }

            this.File = file;

            // load the libary to memory
            UnsafeMethods.SetErrorMode(0);
            this.LibaryPointer = UnsafeMethods.LoadLibrary(this.File.FullName);
            if (this.LibaryPointer.ToInt32() == 0)
            {
                var errorCode = Marshal.GetLastWin32Error();
                var ex = new Win32Exception(errorCode);
                throw new Exception("error " + ex, ex);
            }
            Debug.WriteLine("Loaded library library pointer points to address " + this.LibaryPointer.ToInt32().ToString(CultureInfo.InvariantCulture));

            // get the procedure address
            const string ProcedureName = "plugMain";
            UnsafeMethods.SetErrorMode(0);
            Debug.WriteLine("*** Trying to find procedure '" + ProcedureName + "' ***");
            this.PlugMainPointer = UnsafeMethods.GetProcAddress(this.LibaryPointer, ProcedureName);

            if (this.PlugMainPointer.ToInt32() == 0)
            {
                Debug.WriteLine("-failure : " + this.PlugMainPointer);
                var errorCode = Marshal.GetLastWin32Error();
                var ex = new Win32Exception(errorCode);
                Debug.WriteLine("-error code : " + errorCode + " , " + ex);
                return;
            }
            Debug.WriteLine("-success , plugMainPointer is : " + this.PlugMainPointer.ToInt32().ToString(CultureInfo.InvariantCulture));

            // get the acces point to inject functions into the dll
            try
            {
                Debug.WriteLine("*** Convert the unmanaged 'dll.plugMain()' function pointer to .Net delegate 'plugMainInvoker' ***");
                this._plugMainInvoker = (PlugMainInvoker)Marshal.GetDelegateForFunctionPointer(this.PlugMainPointer, typeof(PlugMainInvoker));
                this._plugMainInvoker2 = (PlugMainInvoker2)Marshal.GetDelegateForFunctionPointer(this.PlugMainPointer, typeof(PlugMainInvoker2));
                Debug.WriteLine("- success !!!!");
            }
            catch (Exception ex)
            {
                Debug.WriteLine("- failure : " + ex);
            }

            this.GetInfo();
            this.GetNumberOfParameters();
            this.GetParameterNames();
            this.GetParameterDefaults();
            this.GetParameterTypes();
            this.GetPluginCaps();
            this.GetExtendedInfo();
        }
        #endregion

        public void Initialize()
        {
            Debug.WriteLine("*** You choosed Initialize ***");
            Debug.WriteLine("-val 1 (function code) : 1 (Initialize)");
            Debug.WriteLine("-val 2 (input value) : 0 (None , not used)");
            Debug.WriteLine("-val 3 (instance id) : 0 (None , this is a global function)");
            Debug.WriteLine("Output val : A Succes code when initialized");

            try
            {
                Debug.WriteLine("*** Trying invoke ***");
                var invokeResult = (UInt32)this._plugMainInvoker.Invoke(1, 0, 0);
                if (invokeResult == (UInt32)ResultCode.FfSuccess)
                {
                    Debug.WriteLine("Initialize the plugin");
                    this.Initialized = true;
                }
                else
                {
                    Debug.WriteLine("Initialization of the plugin failed.");
                    this.Initialized = false;
                }
            }
            catch (Exception ex)
            {
                this.Initialized = false;
                throw new Exception("- failure : " + ex);
            }
        }

        public void DeInitialize()
        {
            Debug.WriteLine("*** You choosed DeInitialize ***");
            Debug.WriteLine("-val 1 (function code) : 2 (DeInitialize)");
            Debug.WriteLine("-val 2 (input value) : 0 (None , not used)");
            Debug.WriteLine("-val 3 (instance id) : 0 (None , this is a global function)");
            Debug.WriteLine("Output val : A Succes code when initialized");

            try
            {
                Debug.WriteLine("*** Trying invoke ***");
                var invokeResult = (UInt32)this._plugMainInvoker.Invoke(2, 0, 0);
                if (invokeResult == (UInt32)ResultCode.FfSuccess)
                {
                    Debug.WriteLine("DeInitialized the plugin");
                    this.Initialized = false;
                }
                else
                {
                    Debug.WriteLine("DeInitialization of the plugin failed");
                    this.Initialized = true;
                }
            }
            catch (Exception ex)
            {
                this.Initialized = true;
                throw new Exception("- failure : " + ex);
            }
        }

        #region Initialization functions
        /// <summary>
        /// Gets the info.
        /// Gets information about the plugin - version, unique id, short name and type (see PluginInfoStruct). 
        /// This function should be identical in all future versions of the FreeFrame API. 
        ///  HOST: Call this function first to get version information. The version defines the other function codes that are supported.  
        /// 
        /// This 'managed plugin' remembers the info from PlugInfoStruct.
        /// </summary>
        private void GetInfo()
        {
            Debug.WriteLine("*** You choosed GetInfo ***");
            Debug.WriteLine("-val 1 (function code) : 0 (GetInfo)");
            Debug.WriteLine("-val 2 (input value) : 0 (None , not used)");
            Debug.WriteLine("-val 3 (instance id) : 0 (None , this is a global function)");
            Debug.WriteLine("Output val : Pointer to a Plugin Info Structure");
            
            try
            {
                Debug.WriteLine("*** Trying invoke ***");
                var pointerToPluginInfoStructPointer = this._plugMainInvoker.Invoke(0, 0, 0);
                Debug.WriteLine("- pointer to plugin info structure is : " + pointerToPluginInfoStructPointer);
                Debug.WriteLine("*** trying to marshal that pointer to a .net structure ***");
                var pluginInfoStructure = (PluginInfoStruct)Marshal.PtrToStructure(pointerToPluginInfoStructPointer, typeof(PluginInfoStruct));
                Debug.WriteLine("the .net Structure contains : ");
                Debug.WriteLine("-Api Major Version : " + pluginInfoStructure.ApiMajorVersion);
                this.MajorVersion = pluginInfoStructure.ApiMajorVersion.ToString(CultureInfo.InvariantCulture);
                Debug.WriteLine("-Api Minor Version : " + pluginInfoStructure.ApiMinorVersion);
                this.MinorVersion = pluginInfoStructure.ApiMinorVersion.ToString(CultureInfo.InvariantCulture);
                Debug.WriteLine("-PluginUniqueID : " + pluginInfoStructure.UniqueId);
                this.UniqueID = pluginInfoStructure.UniqueId;
                Debug.WriteLine("-PluginName : " + pluginInfoStructure.PluginName);
                this.PluginName = pluginInfoStructure.PluginName;
                Debug.WriteLine("-PluginType : " + pluginInfoStructure.PluginType);
                switch (pluginInfoStructure.PluginType)
                {
                    case 0:
                        Debug.WriteLine("-PluginType is a effect");
                        this.PluginType = PluginType.Effect;
                        break;
                    case 1:
                        Debug.WriteLine("-PluginType is a source");
                        this.PluginType = PluginType.Source;
                        break;
                }
            }
            catch (Exception ex)
            {
                throw new Exception("- failure : " + ex);
            }
        }

        /// <summary>
        /// Gets the number of parameters.
        /// 
        /// PLUGIN: 
        /// -Plugin developers should normally expect hosts to expose up to 8 parameters.
        /// -Some hosts may only expose 1 or 4 parameters, some may expose larger numbers. 
        /// -All parameters should have sensible defaults incase the user is unable to control them from the host.  
        /// 
        /// HOST:   
        /// -Host developers should try to implement at least the first 4 parameters. 8 is the recommended number to expose.  
        /// 
        /// This 'managed plugin' remembers this info 
        /// </summary>
        private void GetNumberOfParameters()
        {
            
            Debug.WriteLine("*** You pressed 4 Get Number of Parameters ***");
            Debug.WriteLine("-val 1 (function code) : 4 (Get Number of Parameters)");
            Debug.WriteLine("-val 2 (input value) : 0 (None , not used)");
            Debug.WriteLine("-val 3 (instance id) : 0 (None , this is a global function)");
            Debug.WriteLine("Output val : Pointer to Number of Parameters");
            try
            {
                Debug.WriteLine("*** Trying invoke ***");
                this.NumberOfParameters = (UInt32)this._plugMainInvoker.Invoke(4, 0, 0);
                Debug.WriteLine("-Number of Parameters is : " + this.NumberOfParameters);
            }
            catch (Exception ex)
            {
                Debug.WriteLine("- failure : " + ex);
            }
        }

        /// <summary>
        /// Gets the parameter names.
        /// 32-bit pointer to ParameterName (16 byte char array) containing the name of parameter specified by index. Returns FF_FAIL on error. 
        /// </summary>
        private void GetParameterNames()
        {
            
            this.Parameters.Clear();
            Debug.WriteLine("*** You pressed 5 Get parameter name ***");
            Debug.WriteLine("-we have " + this.NumberOfParameters + " number of parameters");
            Debug.WriteLine("*** lets check each parameter ***");
            Debug.WriteLine(string.Empty);

            for (var a = 0; a < this.NumberOfParameters; a++)
            {
                Debug.WriteLine("-val 1 (function code) : 5 (Get parameter Name)");
                Debug.WriteLine("-val 2 (input value) : " + a + " (Parameter number)");
                Debug.WriteLine("-val 3 (instance id) : 0 (None , this is a global function)");
                Debug.WriteLine("Output val : Pointer to parameter name");
                try
                {
                    Debug.WriteLine("*** Trying invoke ***");
                    var parameterNamePointer = this._plugMainInvoker.Invoke(5, (UInt32)a, 0);
                    Debug.WriteLine("-parameterNamePointer is : " + parameterNamePointer);

                    Debug.WriteLine("*** Trying to marshal the pointer to a string ***");
                    var parameterName = Marshal.PtrToStringAnsi(parameterNamePointer, 16);
                    Debug.WriteLine("-parameter name is : " + parameterName);
                    var parameter = new Parameter { Name = parameterName };
                    this.Parameters.Add(parameter);
                    Debug.WriteLine(string.Empty);
                }
                catch (Exception ex)
                {
                    Debug.WriteLine("- failure : " + ex);
                }

                Debug.WriteLine(string.Empty);
            }
        }

        /// <summary>
        /// Gets the parameter defaults.
        /// ParameterDefaultValue indicating the default value of parameter specified by index. 
        /// For float parameters, the return value is a 32-bit float (|0| smaller then |value| smaller then |1|). 
        /// Return value for text parameters is a 32-bit pointer to a null terminated string. 
        /// Returns FF_FAIL on error. 
        /// </summary>
        private void GetParameterDefaults()
        {
            
            Debug.WriteLine("*** You pressed 6 GetParameterDefault ***");
            Debug.WriteLine("-we have " + this.NumberOfParameters + " number of parameters");
            Debug.WriteLine("*** lets check each parameter ***");
            Debug.WriteLine(string.Empty);

            for (var a = 0; a < this.NumberOfParameters; a++)
            {
                Debug.WriteLine("-val 1 (function code) : 6 (Get Parameter Default)");
                Debug.WriteLine("-val 2 (input value) : " + a + " (Parameter number)");
                Debug.WriteLine("-val 3 (instance id) : 0 (None , this is a global function)");
                Debug.WriteLine("Output val : ParameterDefaultValue");

                try
                {
                    Debug.WriteLine("*** Trying invoke ***");
                    var invokeResult = (UInt32)this._plugMainInvoker.Invoke(6, (UInt32)a, 0);
                    var byteArray = BitConverter.GetBytes(invokeResult);
                    var floatValue = BitConverter.ToSingle(byteArray, 0);

                    const string TypeString = "float (0 < value < 1)";
                    Debug.WriteLine("default value is a " + TypeString + " : " + floatValue);
                    this.Parameters[a].DefaultValue = floatValue;
                    this.Parameters[a].Value = floatValue;
                }
                catch (Exception ex)
                {
                    Debug.WriteLine("- failure : " + ex);
                }

                Debug.WriteLine(string.Empty);
            }
        }

        /// <summary>
        /// Hosts may decide to present an appropriate visual interface depending on the parameter type. 
        /// All FreeFrame data for the moment is passed as 32-bit floats from 0-1, except text data. 
        /// 
        /// Return value:
        /// ParameterType value which tells the host what kind of data the parameter index is.
        /// Returns FF_FAIL on error. 
        /// </summary>
        private void GetParameterTypes()
        {
            Debug.WriteLine("*** You pressed 15 Get Parameter Type ***");
            Debug.WriteLine("-we have " + this.NumberOfParameters + " number of parameters");
            Debug.WriteLine("*** lets check each parameter ***");
            Debug.WriteLine(string.Empty);

            for (var a = 0; a < this.NumberOfParameters; a++)
            {
                Debug.WriteLine("-val 1 (function code) : 6 (Get Parameter Default)");
                Debug.WriteLine("-val 2 (input value) : " + a + " (Parameter number)");
                Debug.WriteLine("-val 3 (instance id) : 0 (None , this is a global function)");
                Debug.WriteLine("Output val : ParameterDefaultValue");

                try
                {
                    Debug.WriteLine("*** Trying invoke ***");
                    var invokeResult = (UInt32)this._plugMainInvoker.Invoke(15, (UInt32)a, 0);
                    var byteArray = BitConverter.GetBytes(invokeResult);
                    var typeValue = BitConverter.ToUInt32(byteArray, 0);
                    switch (typeValue)
                    {
                        case 0:
                            this.Parameters[a].ParameterType = ParameterType.Boolean;
                            break;
                        case 1:
                            this.Parameters[a].ParameterType = ParameterType.Event;
                            break;
                        case 2:
                            this.Parameters[a].ParameterType = ParameterType.Red;
                            break;
                        case 3:
                            this.Parameters[a].ParameterType = ParameterType.Green;
                            break;
                        case 4:
                            this.Parameters[a].ParameterType = ParameterType.Blue;
                            break;
                        case 5:
                            this.Parameters[a].ParameterType = ParameterType.XPos;
                            break;
                        case 6:
                            this.Parameters[a].ParameterType = ParameterType.Ypos;
                            break;
                        case 10:
                            this.Parameters[a].ParameterType = ParameterType.Standard;
                            break;
                        case 100:
                            this.Parameters[a].ParameterType = ParameterType.Text;
                            break;
                    }
                    Debug.WriteLine("Paramater '" + this.Parameters[a].Name + "' is a type of : " + this.Parameters[a].ParameterType);
                }
                catch (Exception ex)
                {
                    Debug.WriteLine("- failure : " + ex);
                }

                Debug.WriteLine(string.Empty);
            }
        }

        /// <summary>
        /// Bitdepth format of the video: 
        /// The host asks the plugin if it is capable of its favorite bit depth, and uses that if it is available.
        ///  If not the host may decide not to use the plugin and deinitialise it, 
        /// or it may enquire again to see if a second choice format is supported. 
        /// 
        /// Return value: 
        /// Indicates capability of feature specified by capsIndex. 
        /// See PluginCapsIndex for a more detailed description of the individual capabilities. 
        /// In every case, the return value can be represented as a 32-bit unsigned integer. 
        /// </summary>
        private void GetPluginCaps()
        {
            
            Debug.WriteLine("*** You pressed 10 GetPluginCaps ***");
            Debug.WriteLine("*** Lets try to get all caps ***");
            Debug.WriteLine(string.Empty);

            try
            {
                this.CheckFor16BitSupport();
                this.CheckFor24BitSupport();
                this.CheckFor32BitSupport();
                this.CheckForFfSupport();
            }
            catch (Exception ex)
            {
                Debug.WriteLine("- failure : " + ex);
            }

            Debug.WriteLine(string.Empty);
        }

        /// <summary>
        /// Gets the extended info.
        /// 
        /// Return value: 
        /// 32-bit pointer to PluginExtendedInfoStruct or FF_FAIL on error. 
        /// </summary>
        private void GetExtendedInfo()
        {
            
            Debug.WriteLine("*** You pressed 13 GetExtendedInfo ***");
            Debug.WriteLine("-val 1 (function code) : 13 (GetExtendedInfo)");
            Debug.WriteLine("-val 2 (input value) : 0 (None , not used)");
            Debug.WriteLine("-val 3 (instance id) : 0 (None , this is a global function)");
            Debug.WriteLine("Output val : Pointer to a Plugin Extended Info Structure");
            try
            {
                Debug.WriteLine("*** Trying invoke ***");
                var pointerToPluginInfoStructPointer = this._plugMainInvoker.Invoke(13, 0, 0);
                Debug.WriteLine("- pointer to plugin info structure is : " + pointerToPluginInfoStructPointer);
                Debug.WriteLine("*** trying to marshal that pointer to a .net structure ***");
                var pluginInfoStructure = (PluginExtendedInfoStruct)Marshal.PtrToStructure(pointerToPluginInfoStructPointer, typeof(PluginExtendedInfoStruct));
                
                Debug.WriteLine("- Plugin Major Version : " + pluginInfoStructure.PluginMajorVersion);
                this.ExtPluginMajorVersion = pluginInfoStructure.PluginMajorVersion.ToString(CultureInfo.InvariantCulture);
                
                Debug.WriteLine("- Plugin Minor Version : " + pluginInfoStructure.PluginMinorVersion);
                this.ExtPluginMinorVersion = pluginInfoStructure.PluginMinorVersion.ToString(CultureInfo.InvariantCulture);

                Debug.WriteLine("- Plugin Description : " + pluginInfoStructure.PluginMinorVersion);
                this.ExtDescription = Marshal.PtrToStringAnsi(pluginInfoStructure.Description);

                Debug.WriteLine("- Plugin About : " + pluginInfoStructure.PluginMinorVersion);
                this.ExtAbout = Marshal.PtrToStringAnsi(pluginInfoStructure.About);
                
                Debug.WriteLine("- Extended data size : " + pluginInfoStructure.FreeFrameExtendedDataSize);
                this.ExtDataSize = pluginInfoStructure.FreeFrameExtendedDataSize;
            }
            catch (Exception ex)
            {
                Debug.WriteLine("- failure : " + ex);
            }
        }
        #endregion

        #region Instance Specific Functions
        // ReSharper disable MemberCanBePrivate.Global
        public bool Instantiate(VideoInfoStruct videoInfoStruct)
        {
            Debug.WriteLine("*** You choosed Instantiate ***");
            Debug.WriteLine("-val 1 (function code) : 11 (Instantiate)");
            Debug.WriteLine("-val 2 (input value) : 32-bit pointer to VideoInfoStruct.");
            Debug.WriteLine("-val 3 (instance id) : 0 (None , this is a global function)");
            Debug.WriteLine("Output val : An InstanceIdentifier for the newly instantiated effect. Will be used as instanceID for future function calls.");

            switch (videoInfoStruct.BitDepth)
            {
                    // 16 bit 5-6-5
                case 0:
                    if (this.Support16Bit == false) return false;
                    break;

                    // 24 bit packed
                case 1:
                    if (this.Support24Bit == false) return false;
                    break;

                    // 32 bit
                case 2:
                    if (this.Support32Bit == false) return false;
                    break;
            }

            try
            {
                Debug.WriteLine("*** Trying invoke ***");
                var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(VideoInfoStruct)));
                Marshal.StructureToPtr(videoInfoStruct, ptr, false);
                var invokeResult = this._plugMainInvoker2.Invoke(11, ptr, 0);

                if ((UInt32)invokeResult == (UInt32)ResultCode.FfFail)
                {
                    Debug.WriteLine("Error in instancing the plugin.");
                    return false;
                }

                this.InstanceIdentifier = (UInt32)invokeResult;
                return true;
            }
            catch (Exception ex)
            {
                this.Initialized = false;
                throw new Exception("- failure : " + ex);
            }
        }

        public bool Instantiate(Bitmap bitmap)
        {
            var depth = -1;
            switch (bitmap.PixelFormat)
            {
                case PixelFormat.Format16bppRgb565:
                    depth = 0;
                    break;

                case PixelFormat.Format24bppRgb:
                    depth = 1;
                    break;

                case PixelFormat.Format32bppArgb:
                    depth = 2;
                    break;
            }

            if (depth == -1) return false;

            var str = new VideoInfoStruct
            {
                BitDepth = (UInt32)depth,
                FrameWidth = (UInt32)bitmap.Width,
                FrameHeight = (UInt32)bitmap.Height,
                Orientation = 1
            };

            return this.Instantiate(str);

        }

        public void DeInstantiate()
        {
            Debug.WriteLine("*** You choosed DeInstantiate ***");
            Debug.WriteLine("-val 1 (function code) : 12 (DeInstantiate)");
            Debug.WriteLine("-val 2 (input value) : 0 (None , not used)");
            Debug.WriteLine("-val 3 (instance id) : instanceId to close");
            Debug.WriteLine("Output val : Result.");

            try
            {
                Debug.WriteLine("*** Trying invoke ***");
                var invokeResult = (UInt32)this._plugMainInvoker.Invoke(12, 0, this.InstanceIdentifier);

                if (invokeResult == (UInt32)ResultCode.FfSuccess)
                {
                    Debug.WriteLine("DeInstanced the plugin");
                    this.Initialized = false;
                }
                else
                {
                    Debug.WriteLine("DeInstanced of the plugin failed");
                    this.Initialized = true;
                }
            }
            catch (Exception ex)
            {
                this.Initialized = false;
                throw new Exception("- failure : " + ex);
            }
        }

        public void GetParametersDisplay()
        {
            Debug.WriteLine("*** You choosed GetParameterDisplay ***");
            Debug.WriteLine("-val 1 (function code) : 7 (GetParameterDisplay)");
            Debug.WriteLine("-val 2 (input value) : parameter number");
            Debug.WriteLine("-val 3 (instance id) : instanceId");
            Debug.WriteLine("Output val : Result.");

            try
            {
                for (UInt32 parameter = 0; parameter < this.NumberOfParameters; parameter++)
                {
                    var invokeResult = this._plugMainInvoker.Invoke(7, parameter, this.InstanceIdentifier);

                    if ((UInt32)invokeResult == (UInt32)ResultCode.FfFail)
                    {
                        Debug.WriteLine("Getting the display-value failed, for parameter : " + parameter);
                    }

                    var value = Marshal.PtrToStringAnsi(invokeResult);
                    this.Parameters[(int)parameter].ParameterDisplay = value;
                }
            }
            catch (Exception ex)
            {
                this.Initialized = false;
                throw new Exception("- failure : " + ex);
            }
        }

        public void SetParamater(UInt32 parameterNumber,  float parameterValue)
        {
            Debug.WriteLine("*** You choosed SetParameter ***");
            Debug.WriteLine("-val 1 (function code) : 8 (SetParameter)");
            Debug.WriteLine("-val 2 (input value) : Pointer to SetParameterStruct");
            Debug.WriteLine("-val 3 (instance id) : instanceId");
            Debug.WriteLine("Output val : Result.");

            try
            {
                Debug.WriteLine("Setting parameter : " + parameterNumber + " with value " + parameterValue);
                var str = new SetParameterStruct { ParameterNumber = parameterNumber, ParameterValue = parameterValue };
                var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SetParameterStruct)));
                Marshal.StructureToPtr(str, ptr, false);
                var invokeResult = (UInt32)this._plugMainInvoker2.Invoke(8, ptr, this.InstanceIdentifier);

                if (invokeResult == (UInt32)ResultCode.FfFail)
                {
                    Debug.WriteLine("Setting parameter : " + parameterNumber + " was a failure.");
                }

                if (invokeResult == (UInt32)ResultCode.FfSuccess)
                {
                    Debug.WriteLine("Setting parameter : " + parameterNumber + " was a success.");
                }

            }
            catch (Exception ex)
            {
                this.Initialized = false;
                throw new Exception("- failure : " + ex);
            }
        }

        public void GetParametersValue()
        {
            Debug.WriteLine("*** You choosed GetParameterValue ***");
            Debug.WriteLine("-val 1 (function code) : 9 (GetParameterValue");
            Debug.WriteLine("-val 2 (input value) : parameter number");
            Debug.WriteLine("-val 3 (instance id) : instanceId");
            Debug.WriteLine("Output val : Result.");

            try
            {
                for (UInt32 parameter = 0; parameter < this.NumberOfParameters; parameter++)
                    unsafe
                    {
                        var invokeResult = this._plugMainInvoker.Invoke(9, parameter, this.InstanceIdentifier);

                        if ((UInt32)invokeResult == (UInt32)ResultCode.FfFail)
                        {
                            Debug.WriteLine("Getting the display-value failed, for parameter : " + parameter);
                            return;
                        }

                        var r = invokeResult.ToPointer();
                        var f = *(float *)(&r);
                        //var f = (float)invokeResult;
                        this.Parameters[(int)parameter].Value = f;
                    }
            }
            catch (Exception ex)
            {
                this.Initialized = false;
                throw new Exception("- failure : " + ex);
            }
            
        }
        // ReSharper restore MemberCanBePrivate.Global
        #endregion

        #region Processing
        /// <summary>
        /// Processes the frame.
        /// </summary>
        /// <param name="pointerToFrame">The pointer to frame. 32-bit pointer to byte array containing frame of video. </param>
        /// <remarks>
        /// PLUGIN: Process a frame of video 'in place'. This is also the basic frameport for source plugins. 
        /// HOST  : pFrame needs to be a valid pointer throughout this call as the plugin processes the frame 'in place'.  
        /// </remarks>
        public void ProcessFrame(IntPtr pointerToFrame)
        {
            Debug.WriteLine("*** You choosed ProcessFrame ***");
            Debug.WriteLine("-val 1 (function code) : 3 (ProcessFrame)");
            Debug.WriteLine("-val 2 (input value) : not used");
            Debug.WriteLine("-val 3 (instance id) : not used");
            Debug.WriteLine("Output val : Returns sucess or fail.");

            try
            {
                var invokeResult = (UInt32)this._plugMainInvoker2.Invoke(3, pointerToFrame, this.InstanceIdentifier);

                if (invokeResult == (UInt32)ResultCode.FfFail)
                {
                    Debug.WriteLine("Process frame was a failure.");
                }

                if (invokeResult == (UInt32)ResultCode.FfSuccess)
                {
                    Debug.WriteLine("Process frame was a success.");
                }

            }
            catch (Exception ex)
            {
                this.Initialized = false;
                throw new Exception("- failure : " + ex);
            }
        }

        /// <summary>
        /// The ProcessFrameCopy function performs a source->dest buffer frame process in effects plugins. 
        /// </summary>
        /// <param name="pointerToFrame">The pointer to frame. 32-bit pointer to byte array containing frame of video. </param>
        /// <remarks>
        /// Effect Plugins must support the processFrame (in-place) method of processing. 
        /// This method is optional. Plugins should specify if they are capable of this kind of processing 
        /// - and if they have optimized a particular method - in the Plugin Caps system. 
        /// Source Plugins should not use this function as they do not require input frames. 
        /// Source plugins should just use processFrame   
        /// </remarks>
        public void ProcessFrameCopy(IntPtr pointerToFrame)
        {
            Debug.WriteLine("*** You choosed ProcessFrame ***");
            Debug.WriteLine("-val 1 (function code) : 3 (ProcessFrame)");
            Debug.WriteLine("-val 2 (input value) : not used");
            Debug.WriteLine("-val 3 (instance id) : not used");
            Debug.WriteLine("Output val : Returns sucess or fail.");

            try
            {
                var invokeResult = (UInt32)this._plugMainInvoker.Invoke(12, (UInt32)pointerToFrame, this.InstanceIdentifier);

                if (invokeResult == (UInt32)ResultCode.FfFail)
                {
                    Debug.WriteLine("Process frame copy was a failure.");
                }

                if (invokeResult == (UInt32)ResultCode.FfSuccess)
                {
                    Debug.WriteLine("Process frame copy was a success.");
                }

            }
            catch (Exception ex)
            {
                this.Initialized = false;
                throw new Exception("- failure : " + ex);
            }
        }
        #endregion

        #region Helper Functions
        private void CheckFor16BitSupport()
        {
            const PluginCapsIndex Cap = PluginCapsIndex.I16Bit;
            Debug.WriteLine("-val 1 (function code) : 10 (GetPluginCaps)");
            Debug.WriteLine("-val 2 (input value) : " + Cap + " (PluginCapsIndex)");
            Debug.WriteLine("-val 3 (instance id) : 0 (None , this is a global function)");
            Debug.WriteLine("Output val : Indicates capability of feature specified by capsIndex.");
            Debug.WriteLine("*** Trying invoke ***");
            var invokeResult = (UInt32)this._plugMainInvoker.Invoke(10, (UInt32)Cap, 0);

            if (invokeResult == (UInt32)ResultCode.FfSupported)
            {
                Debug.WriteLine("16 bit 5-6-5 is supported");
                this.Support16Bit = true;
            }
            else
            {
                Debug.WriteLine("16 bit 5-6-5 is not supported");
                this.Support16Bit = false;
            }
        }

        private void CheckFor24BitSupport()
        {
            const PluginCapsIndex Cap = PluginCapsIndex.I24Bit;
            Debug.WriteLine("-val 1 (function code) : 10 (GetPluginCaps)");
            Debug.WriteLine("-val 2 (input value) : " + Cap + " (PluginCapsIndex)");
            Debug.WriteLine("-val 3 (instance id) : 0 (None , this is a global function)");
            Debug.WriteLine("Output val : Indicates capability of feature specified by capsIndex.");
            Debug.WriteLine("*** Trying invoke ***");
            var invokeResult = (UInt32)this._plugMainInvoker.Invoke(10, (UInt32)Cap, 0);
            if (invokeResult == (UInt32)ResultCode.FfSupported)
            {
                Debug.WriteLine("24 bit packed is supported");
                this.Support24Bit = true;
            }
            else
            {
                Debug.WriteLine("24 bit packed is not supported");
                this.Support24Bit = false;
            }
        }

        private void CheckFor32BitSupport()
        {
            const PluginCapsIndex Cap = PluginCapsIndex.I32Bit;
            Debug.WriteLine("-val 1 (function code) : 10 (GetPluginCaps)");
            Debug.WriteLine("-val 2 (input value) : " + Cap + " (PluginCapsIndex)");
            Debug.WriteLine("-val 3 (instance id) : 0 (None , this is a global function)");
            Debug.WriteLine("Output val : Indicates capability of feature specified by capsIndex.");
            Debug.WriteLine("*** Trying invoke ***");
            var invokeResult = (UInt32)this._plugMainInvoker.Invoke(10, (UInt32)Cap, 0);
            if (invokeResult == (UInt32)ResultCode.FfSupported)
            {
                Debug.WriteLine("32 bit (or 24 bit aligned) is supported");
                this.Support32Bit = true;
            }
            else
            {
                Debug.WriteLine("32 bit (or 24 bit aligned is not supported");
                this.Support32Bit = false;
            }
        }

        private void CheckForFfSupport()
        {
            var cap = PluginCapsIndex.PlugProcessFrameCopy;
            Debug.WriteLine("-val 1 (function code) : 10 (GetPluginCaps)");
            Debug.WriteLine("-val 2 (input value) : " + cap + " (PluginCapsIndex)");
            Debug.WriteLine("-val 3 (instance id) : 0 (None , this is a global function)");
            Debug.WriteLine("Output val : Indicates capability of feature specified by capsIndex.");
            Debug.WriteLine("*** Trying invoke ***");
            var invokeResult = (UInt32)this._plugMainInvoker.Invoke(10, (UInt32)cap, 0);
            if (invokeResult == (UInt32)ResultCode.FfSupported)
            {
                Debug.WriteLine("'process frame copy' is supported");
                this.SupportProcessFrameCopy = true;
                Debug.WriteLine(string.Empty);

                Debug.WriteLine("*** lets examine which 'process frame copy'-details are supported ***");
                Debug.WriteLine(string.Empty);

                cap = PluginCapsIndex.MinimumInputFrames;
                Debug.WriteLine("-val 1 (function code) : 10 (GetPluginCaps)");
                Debug.WriteLine("-val 2 (input value) : " + cap + " (PluginCapsIndex)");
                Debug.WriteLine("-val 3 (instance id) : 0 (None , this is a global function)");
                Debug.WriteLine("Output val : Indicates capability of feature specified by capsIndex.");
                Debug.WriteLine("*** Trying invoke ***");
                invokeResult = (UInt32)this._plugMainInvoker.Invoke(10, (UInt32)cap, 0);
                if (invokeResult == (UInt32)ResultCode.FfFalse)
                {
                    Debug.WriteLine("minimum number of frames is not supported");
                    this.SupportMinimumNumberOfFrames = false;
                    this.SupportMaximumNumberOfFramesNeeded = 0;
                }
                else
                {
                    Debug.WriteLine("minimum number of frames is : " + invokeResult);
                    this.SupportMinimumNumberOfFrames = true;
                    this.SupportMinimumNumberOfFramesNeeded = invokeResult;
                }

                Debug.WriteLine(string.Empty);

                cap = PluginCapsIndex.MaximumInputFrames;
                Debug.WriteLine("-val 1 (function code) : 10 (GetPluginCaps)");
                Debug.WriteLine("-val 2 (input value) : " + cap + " (PluginCapsIndex)");
                Debug.WriteLine("-val 3 (instance id) : 0 (None , this is a global function)");
                Debug.WriteLine("Output val : Indicates capability of feature specified by capsIndex.");
                Debug.WriteLine("*** Trying invoke ***");
                invokeResult = (UInt32)this._plugMainInvoker.Invoke(10, (UInt32)cap, 0);
                if (invokeResult == (UInt32)ResultCode.FfFalse)
                {
                    Debug.WriteLine("maximum number of frames is not supported");
                    this.SupportMaximumNumberOfFrames = false;
                    this.SupportMaximumNumberOfFramesNeeded = 0;
                }
                else
                {
                    Debug.WriteLine("maximum number of frames is : " + invokeResult);
                    this.SupportMaximumNumberOfFrames = true;
                    this.SupportMaximumNumberOfFramesNeeded = invokeResult;
                }

                Debug.WriteLine(string.Empty);

                cap = PluginCapsIndex.OptimizedForCopyOrInplaceProcessing;
                Debug.WriteLine("-val 1 (function code) : 10 (GetPluginCaps)");
                Debug.WriteLine("-val 2 (input value) : " + cap + " (PluginCapsIndex)");
                Debug.WriteLine("-val 3 (instance id) : 0 (None , this is a global function)");
                Debug.WriteLine("Output val : Indicates capability of feature specified by capsIndex.");
                Debug.WriteLine("*** Trying invoke ***");
                invokeResult = (UInt32)this._plugMainInvoker.Invoke(10, (UInt32)cap, 0);
                switch (invokeResult)
                {
                    case 0:
                        this.OptimizedFor = Optimization.NoPreference;
                        break;
                    case 1:
                        this.OptimizedFor = Optimization.InPlaceProcessingIsFaster;
                        break;
                    case 2:
                        this.OptimizedFor = Optimization.CopyProcessingIsFaster;
                        break;
                    case 3:
                        this.OptimizedFor = Optimization.BothAreOptimized;
                        break;
                }

                Debug.WriteLine("my plugin optimization is : '" + this.OptimizedFor + "'");
                Debug.WriteLine(string.Empty);
            }
            else
            {
                Debug.WriteLine("'process frame copy' is not supported");
                this.SupportProcessFrameCopy = false;
            }
        }

        #endregion

        #region Properties
        // ReSharper disable MemberCanBePrivate.Global
        // ReSharper disable UnusedAutoPropertyAccessor.Global
        /// <summary>
        /// Gets or sets the instance identifier.
        /// Unique identifier for the instance of a plugin. 
        /// For some plugins, this value might represent a pointer to a plugin-specific data structure containing the instance's current state. 
        /// Or it could be an index to a table of all active instances of a plugin, or any other value that uniquely identifies an instance. 
        /// </summary>
        /// <value>The instance identifier.</value>
        public UInt32 InstanceIdentifier { get; set; }

        public bool Initialized { get; private set; }

        public List<Parameter> Parameters { get; private set; }

        /// <summary>
        /// Gets whether plugin is optimized for Copy or InPlace processing.
        /// </summary>
        /// <value>The optimized for.</value>
        public Optimization OptimizedFor { get; private set; }

        /// <summary>
        /// Gets a value indicating whether this plugin supports the 'processFrameCopy' function.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if [support process frame copy]; otherwise, <c>false</c>.
        /// </value>
        public bool SupportProcessFrameCopy { get; private set; }

        public UInt32 SupportMinimumNumberOfFramesNeeded { get; private set; }

        public bool SupportMinimumNumberOfFrames { get; private set; }

        public UInt32 SupportMaximumNumberOfFramesNeeded { get; private set; }

        public bool SupportMaximumNumberOfFrames { get; private set; }

        /// <summary>
        /// Gets or sets a value indicating whether [support16 bit]. 16 bit 5-6-5
        /// </summary>
        /// <value><c>true</c> if [support16 bit]; otherwise, <c>false</c>.</value>
        public bool Support16Bit { get; private set; }

        /// <summary>
        /// Gets or sets a value indicating whether [support24 bit].24 bit packed
        /// </summary>
        /// <value><c>true</c> if [support24 bit]; otherwise, <c>false</c>.</value>
        public bool Support24Bit { get; private set; }

        /// <summary>
        /// Gets or sets a value indicating whether [support32 bit]. 32 bit (or 24 bit aligned)
        /// </summary>
        /// <value><c>true</c> if [support32 bit]; otherwise, <c>false</c>.</value>
        public bool Support32Bit { get; private set; }

        public FileInfo File { get; private set; }

        public IntPtr LibaryPointer { get; private set; }

        public IntPtr PlugMainPointer { get; private set; }

        public string MajorVersion { get; private set; }

        public string MinorVersion { get; private set; }

        public string UniqueID { get; private set; }

        public string PluginName { get; private set; }

        public PluginType PluginType { get; private set; }

        /// <summary>
        ///  Specifies the number of parameters that the plugin implements.
        /// </summary>
        /// <value>The number of parameters.</value>
        public UInt32 NumberOfParameters { get; private set; }

        public string ExtPluginMajorVersion { get; private set; }

        public string ExtPluginMinorVersion { get; private set; }

        public string ExtDescription { get; private set; }

        public string ExtAbout { get; private set; }

        public UInt32 ExtDataSize { get; private set; }
        // ReSharper restore MemberCanBePrivate.Global
        // ReSharper restore UnusedAutoPropertyAccessor.Global
        #endregion
    }
    // ReSharper restore MemberCanBeInternal
}
